#! /usr/bin/python

#
# object types in chain
#

aaf_file         = "aaf-file"
top_level        = "top-level"
lower_level      = "lower-level"
sub_lower_level  = "sub-lower-level"
sub_clip         = "sub-clip"
adjusted_clip    = "adjusted-clip"
template_clip    = "template-clip"
clip             = "clip"
adjusted_clip    = "adjusted-clip"
file_source      = "file-source"
sub_file_source  = "sub-file-source"
import_source    = "import-source"
tape_source      = "tape-source"
film_source      = "film-source"
recording_source = "recording-source"
eoc              = "eoc"
oof              = "oof"

#
# valid derivation chain maps
# 

valid = {}

# valid composition and master references
valid[top_level]       = [     lower_level, sub_clip, adjusted_clip, template_clip, clip ]
valid[lower_level]     = [ sub_lower_level, sub_clip, adjusted_clip, template_clip, clip ]
valid[sub_lower_level] = [                  sub_clip, adjusted_clip, template_clip, clip ]
valid[sub_clip]        = [                            adjusted_clip, template_clip, clip ]
valid[adjusted_clip]   = [                                           template_clip, clip ]

# valid source references
valid[template_clip]    = [ eoc,                                                                                 ]
valid[clip]             = [      oof,     file_source, import_source, tape_source, film_source                   ]
valid[file_source]      = [ eoc, oof, sub_file_source, import_source, tape_source, film_source, recording_source ]
valid[sub_file_source]  = [ eoc, oof,                  import_source, tape_source, film_source, recording_source ]
valid[import_source]    = [ eoc, oof,                                 tape_source, film_source                   ]
valid[tape_source]      = [ eoc, oof,                                              film_source                   ]
valid[film_source]      = [ eoc,                                                                                 ]
valid[recording_source] = [ eoc,                                                                                 ]
valid[eoc]              = [                                                                                      ]
valid[oof]              = [                                                                                      ]

#
# Per object type XML generation routines.
#

def aaf_file_gen(inner):
    xmllist = []
    xmllist.append( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" )
    xmllist.append( "<!DOCTYPE aaf-file SYSTEM \"EPDerivationChain.dtd\">\n" )
    xmllist.append ("<!-- THIS FILE IS GENERATED BY GenDerivationTestXml.py -->\n")
    xmllist.append( "<aaf-file>\n" )
    xmllist.append( inner )
    xmllist.append( "</aaf-file>\n" )
    return "".join( xmllist )

def top_level_sequence_gen(name,inner):
    xmllist = []
    xmllist.append( "<top-level name=\"%s\">\n" % name )
    xmllist.append( "<timeline-mob-slot edit-rate-numerator=\"1\" edit-rate-denominator=\"1\">\n" )
    xmllist.append( "<sequence length=\"1\"  type=\"none\">\n" );
    xmllist.append( inner )
    xmllist.append( "</sequence>\n" )
    xmllist.append( "</timeline-mob-slot>\n" )
    xmllist.append( "</top-level>\n" )
    return "".join( xmllist )

def mob_gen(type,name,inner):
    xmllist = []
    xmllist.append( "<source-clip length=\"1\" track-type=\"none\">\n" )
    xmllist.append( "<%s name=\"%s\">\n" % (type,name) )
    xmllist.append( "<timeline-mob-slot edit-rate-numerator=\"1\" edit-rate-denominator=\"1\">\n" )
    xmllist.append( inner )
    xmllist.append( "</timeline-mob-slot>\n" )
    xmllist.append( "</%s>\n" % type )
    xmllist.append( "</source-clip>\n" )
    return "".join( xmllist )

def end_gen(type):
    xml = "<%s length=\"1\" track-type=\"none\"/>\n" % type
    return xml

# xmlgen is a dictionary of functions that generate a fragment of xml
# for an object. The lamda functions all take one argument that is a
# tuple as follows: (name, innerxml). Where name is the name of the
# type (not used in all cases) and innerxml is the contained xml
# fragment.

xmlgen = {}
xmlgen[aaf_file]         = lambda(args): aaf_file_gen(args[1])
xmlgen[top_level]        = lambda(args): top_level_sequence_gen(args[0], args[1])
xmlgen[lower_level]      = lambda(args): mob_gen(lower_level, args[0], args[1])
xmlgen[sub_lower_level]  = lambda(args): mob_gen(lower_level, args[0], args[1])
xmlgen[sub_clip]         = lambda(args): mob_gen(sub_clip, args[0], args[1])
xmlgen[adjusted_clip]    = lambda(args): mob_gen(adjusted_clip, args[0], args[1])
xmlgen[template_clip]    = lambda(args): mob_gen(template_clip, args[0], args[1])
xmlgen[clip]             = lambda(args): mob_gen(clip, args[0], args[1])
xmlgen[file_source]      = lambda(args): mob_gen(file_source, args[0], args[1])
xmlgen[sub_file_source]  = lambda(args): mob_gen(file_source, args[0], args[1])
xmlgen[import_source]    = lambda(args): mob_gen(import_source, args[0], args[1])
xmlgen[tape_source]      = lambda(args): mob_gen(tape_source, args[0], args[1])
xmlgen[film_source]      = lambda(args): mob_gen(film_source, args[0], args[1])
xmlgen[recording_source] = lambda(args): mob_gen(recording_source, args[0], args[1])
xmlgen[eoc]              = lambda(args): end_gen(eoc)
xmlgen[oof]              = lambda(args): end_gen(oof)

#
# Recursive chain generation function. Performs lookups on chain map
# until end of chain is reached then saves then appends a copy of the
# chain to a list passed as an argument.
#
# derivationChain - the chain the is being built
# chains - the list where the complete chain should be saved
# childMap - the per object type lists of child types

def ChainsGen( derivationChain, chains, childMap ):
    parent = derivationChain[-1]

    assert parent in childMap

    if len(childMap[parent]) == 0:
        chains.append(derivationChain)
        return
    
    for child in childMap[parent]:
        if not child == parent:
            ChainsGen( derivationChain + [child], chains, childMap )

#
# MakeValid - make an xml file that describes a valid file that
# includes every possible valid derivation chain. The file has as
# single top level composition with one track and one sequence.  Each
# valid chain is one component in the sequence.
#

def HighlightObjInChain(which,chain):
    copyChain = []
    for obj in chain:
        if which == obj:
            copyChain.append( "".join(["[", which, "]"]) )
        else:
            copyChain.append( obj )
    copyChain.reverse()
    return copyChain

def MakeValid():
    # Build the list of derivations chains to included in a top-level
    # sequence.
    top_level_chains = []
    for child in valid[top_level]:
        ChainsGen( [child], top_level_chains, valid )

    # Generate the xml fragment for each chain in the top level sequence.
    top_level_chains_xml = []
    top_level_xml = []
    for chain in top_level_chains:
        chain.reverse()
        xml = ""
        for obj in chain:
            name = "/".join([ "valid: %s"% top_level ] + HighlightObjInChain(obj,chain) )
            xml = xmlgen[obj]( (name, xml) );
        top_level_xml.append( xml )
        chain.reverse()

    # Generate the top level composition, and file.
    xml = xmlgen[top_level]( ("top level composition", "".join(top_level_xml)) )
    xml = xmlgen[aaf_file]( ("", xml) )

    print xml
        
#
# The programs main entry point
#

def main():
    MakeValid()


# execute main
main()

